https://mp.weixin.qq.com/s/zhaVGY1yrQB1fECiQMntaQ
hooks 的数据保存在哪里呢？比如 useState 的 state，useRef 的 ref 等。
很容易想到是在 fiber 节点上。

<!-- useEffect原理 -->
useEffect，它是用来封装副作用逻辑的
同样分了 mountEffect 和 updateEffect 两个阶段：
1.mountEffect 里执行了一个 pushEffect 的函数
2.在 updateEffect 里也是，只是多了依赖数组变化的检测逻辑：
3.pushEffect创建了 effect 对象并把它放到了 fiber.updateQueue 上：
4.updateQueue 是个环形链表，有个 lastEffect 来指向最后一个 effect。
为什么要这样设计呢？
因为这样新的 effect 好往后面插入呀，直接设置 lastEffect.next 就行。
也就是说我们执行完 useEffect 之后，就把 effect 串联起来放到了 fiber.updateQueue 上。
5.在 commit 最开始的时候，异步处理的 effect 列表：
取出 fiber.updateQueue，然后从 lastEffect.next 开始循环处理
遍历完一遍 fiber 树，处理完每个 fiber.updateQueue 就处理完了所有的 useEffect 的回调：

那有的同学说了，不在 before mutation、mutation、layout 阶段执行有啥好处呢？
因为异步执行不阻塞渲染呀！

<!-- 总结 -->
useEffect 的 hook 在 render 阶段会把 effect 放到 fiber 的 updateQueue 中，
这是一个  lastEffect.next 串联的环形链表，
然后 commit 阶段会异步执行所有 fiber 节点的 updateQueue 中的 effect。

useLayoutEffect 和 useEffect 差不多，
区别只是它是在 commit 阶段的 layout 阶段同步执行所有 fiber 节点的 updateQueue 中的 effect。

<!-- useState原理 -->
<!-- 总结 -->
1.useState 同样分为 mountState 和 updateState 两个阶段：
2.mountState 会返回 state 和 dispatch 函数，
dispatch 函数里会记录更新到 hook.queue，
然后标记当前 fiber 到根 fiber 的 lane 需要更新，之后调度下次渲染。
3.再次渲染的时候会执行 updateState，会取出 hook.queue，
根据优先级（二进制表示lane）确定最终的 state 返回，这样渲染出的就是最新的结果。


<!-- 同样要分为 mountState 和 updateState 来看：
1.它把 initialState 设置到了 hook.baseState 上，这是 state 最终保存的地方。 -->
<script>
  function mountState(initialState) {
    var hook = mountWorkinProgressHook();
    if (typeofinitialState=== 'function') {
      // $FlowFixMe: Flow doesn't like mixed types
      initialState = initialState();
      hook.memoizedState = hook.baseState = initialsttate
      // 创建了一个 queue，这个是用于多个 setState 的时候记录每次更新的
      var queue= {
        pending: null,
        interleaved: null,
        lanes: NoLanes,
        dispatch: null,
        lastRenderedReducer: basicStateReducer,
        lastRenderedState: initialState
      }
      hook.queue=queue;
      // 返回的第二个值是 dispatch 函数，给他绑定了当前的 fiber 还有那个 queue。
      // 这样，当你再执行返回的 setXxx 函数的时候就会走到 dispatch 逻辑：
      var dispatch = queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber$1,queue)
      return [hook.memoizedState, dispatch];
    }
  }
  function dispatchSetState(fiber, queue, action) {
// 前两个参数 fiber 和 queue 都是 bind 的值，只有第三个参数是传入的新 state，当然，现在还叫 action：
  }
</script>

<!-- useRef原理 -->
1.第一次调用 useRef api
1.1 useRef 第一次调用会走到 mountRef
<script>
  useRef: function (initialValue) {
    currentHookNameInDev = 'useRef'
    mountHookTypesDev();
    //useRef 第一次调用会走到 mountRef
    return mountRef(initialValue);
  }
</script>
1.2在 mountRef 里可以看到它创建了一个 hook 节点，
然后设置了 memoizedState 属性为有 current 属性的对象，也就是 ref 对象。
<script>
  function mountRef(initialValue) {
    // 创建了一个 hook 节点
    var hook = mountWorkInProgressHook();
    {
      var _ref2 = { current: initialValue }
      // 设置了 memoizedState 属性为有 current 属性的对象，也就是 ref 对象
      hook.memoizedState = _ref2
      return _ref2;
    }
  }
</script>

具体创建 hook 链表的过程也很容易看懂：

<script>
  function mountWorkInProgressHook(){
    var hook = {
      memoizedState: null,
      baseState: null,
      baseQueue: null,
      queue: null,
      next: null
    };
    if (workInProgressHook == null) {
      // 第一个节点挂在 fiber 节点的 memoizedState 属性上
      // This is the first hook in the list
      currentlyRenderingFiber$1.memoizedState = workinProgressHook = hook
    } else {
      // 后面的挂在上个节点的 next 属性上。
      // Append to the end of the list
      workinProgressHook = workinProgressHook.next = hlook
      return workInProgressHook;
    }
  }
</script>

2.第二次会走到 updateRef：
<script>
  useRef: function (initialValue){
    currentHookNameInDev = 'useRef';
    updateHookTypesDev();
    return updateRef();
  }
</script>
这里的 updateRef 就是取出 hook 的 momorizedState 的值直接返回了：
<script>
  function updateRef(initialValue) {
    var hook = updateWorkInProgressHook
    return hook.memoizedState;
  }
</script>
所以 useRef 的返回的 ref 对象始终是最开始那个。